﻿#include "WdfReader.h"
#include "../libcc/_window.h"
#include "../libcc/efolder.h"

struct sWdfHead 
{
	ulong id;
	int number;
	uint offset;
};

WdfReader::WdfReader()
{
//	s_ptr = (char*)malloc(s_allocSize = 0x04ff);
}

WdfReader::~WdfReader()
{
	clear();
//	free(s_ptr);
}

static const WdfReader::sIndex* search2(const ulong& uid, const WdfReader::sIndex* indexs, const int& indexsCount)
{
	int begin, end, middle;
	begin = 0, end = indexsCount - 1;
	if (uid < indexs[begin].uid || uid > indexs[end].uid)
	{
		return nullptr;
	}
	// 二分查找(折半查找)
	// 前提是元素有序
	while (begin <= end)
	{
		middle = (begin + end) >> 1;
		if (indexs[middle].uid == uid) return &indexs[middle];
		else if (indexs[middle].uid < uid) begin = middle + 1;
		else end = middle - 1;
	}
	return nullptr;
}

static WdfReader::sWdf getWdfForFile(FILE *open, const char* name)
{
	sWdfHead header;
	int size = sizeof(sWdfHead);
	/*size_t readSize = */fread(&header, 1, size, open);

	WdfReader::sWdf wdf;
//	asert(readSize == size, "wdf read fail: %s", name);
	if (header.id != 0x57444650/*'WDFP'*/)
	{
		if (0)
		{
			asert(false, "invalid wdf: %s", name);
		}
		
		wdf.indexs = nullptr;
		wdf.indexsCount = 0;
		fclose(open);
		return wdf;
	}
	
//	s_wdf.indexs.resize(header.number);
	wdf.indexs = new WdfReader::sIndex[wdf.indexsCount = header.number];
	/*asert(*/fseek(open, header.offset, SEEK_SET)/* == 0, "wdf read fail: %s", name)*/;
	int size2 = sizeof(ulong) * 4;

	/*readSize = */fread(/*&*/wdf.indexs/*[0]*/, size2, header.number, open);
//	asert(readSize == /*size2 * */header.number, "wdf read fail: %s", name);
	wdf.filename = name;
	fclose(open);
	return wdf;
}

static WdfReader::sWdf getWdf(const string &path, const string &FileName/*, ifstream &m_File*/)
{
	FILE* fp = fopen(string(path + FileName).c_str(), "rb");
	if (fp == nullptr)
	{
		ccc_log("wdf open fail: %s", FileName.c_str());
		WdfReader::sWdf wdf;
		wdf.indexs = nullptr;
		wdf.indexsCount = 0;
		// nullptr fp, close what?
		return wdf;
	}
	return getWdfForFile(fp, FileName.data());
}

bool WdfReader::clear()
{
	forr(_wdfs, i)
	{ 
		ccc_delete_array(_wdfs[i].indexs);
		_wdfs[i].indexsCount = 0;

	}
	_wdfs.clear();
	return true;
}


bool WdfReader::load(const string &path, const string &filename)
{
	const auto& wdf = getWdf(path, filename);
	if (wdf.indexs && wdf.indexsCount > 0)
	{
		_wdfs.push_back(wdf);
		if (_path.empty() && !path.empty())
		{
			_path = path;
		}
	}
	return true;
}

bool WdfReader::load(const string &filename)
{
	const auto& istr = filename.find_last_of("/");
	if (istr == string::npos)
	{
		return load("", filename);
	}
	string path = filename.substr(0, istr + 1);
	string name = filename.substr(istr + 1);
	return load(path, name);
}

bool WdfReader::loads(const string &path)
{
	clear();
	auto names = cc::efolder(_path = path, true, 0);
	for (const auto &filename : names)
	{
		load(_path, filename);
	}
	return true;
}


// delete[] pointer by yourself
cpp::sPointer WdfReader::getPointer(const sIndex* index, const string& filename)
{
// 	if (index->size > s_allocSize)
// 	{
// 		s_ptr = (char*)realloc(s_ptr, s_allocSize = index->size);
// 	}
	cpp::sPointer pointer;

#if ccc_not_win32
	bool isBuf;
	MyBuffer *buf = new MyBuffer();
	FILE *fp = nullptr;
	AAsset *asset = getContentsAndriod(_path + filename, isBuf, buf, fp);
	if (asset == nullptr)
	{
		if (isBuf)
		{
			memmove(s_ptr, (char*)buf->buffer() + index->offset, index->size);
			delete buf;
		}
		else
		{
			auto fp = getContents32(_path + filename);
			asert(fp, "wdf open fail: %s", filename.c_str());
			asert(fseek(fp, index->offset, SEEK_SET) == 0, "wdf read fail: %s", filename.c_str());
			size_t readSize = fread(s_ptr, 1, index->size, fp);
			asert(readSize == index->size, "wdf read fail: %s", filename.c_str());
			fclose(fp);
		}
	}
	else
	{
		asert(asset, "wdf open fail: %s", filename.c_str());
//		asert(fseek(fp, index->offset, SEEK_SET) == 0, "wdf read fail: %s", filename.c_str());
		AAsset_seek(asset, index->offset, 0);
		size_t readSize = AAsset_read(asset, s_ptr, index->size);
		asert(readSize == index->size, "wdf read fail: %s", filename.c_str());
		AAsset_close(asset);
	}

#else
	auto fp = fopen(string(_path + filename).c_str(), "rb");
	asert(fp, "wdf open fail: %s", filename.c_str());
	fseek(fp, index->offset, SEEK_SET);
	pointer.ptr = new char[pointer.size = index->size];
	fread(pointer.ptr, 1, pointer.size, fp);
	fclose(fp);
#endif
	
// 	pointer.ptr = s_ptr;
// 	pointer.size = index->size;
	return pointer;
}


// delete[] pointer by yourself
cpp::sPointer WdfReader::getPointer(const ulong& uid, const string& filename /*= ""*/)
{
	cpp::sPointer pointer;
	string str = filename;
	const auto& idx = getIndex(uid, str);
	if (idx == nullptr)
	{
		return pointer;
	}
	return getPointer(idx, str);
}


const WdfReader::sIndex* WdfReader::getIndex(const ulong& uid, string& filename) const
{
	if (filename.empty())
	{
		forv(_wdfs, i)
		{
			const auto& idx = search2(uid, _wdfs[i].indexs, _wdfs[i].indexsCount);
			if (idx)
			{
				filename = _wdfs[i].filename;
				return idx;
			}
		}
	}

	forv(_wdfs, i)
	{
		if (_wdfs[i].filename != filename)
		{
			continue;
		}
		return search2(uid, _wdfs[i].indexs, _wdfs[i].indexsCount);		
	}
	return nullptr;
}

const WdfReader::sIndex* WdfReader::getIndex(const ulong& uid) const
{
	return getIndex(uid, string());
}


